<?php
namespace Tbf\Bundle\SequenceBundle;
use Doctrine\DBAL\Connection;
use Tbf\Component\Arrays\ArrayFunction;

/**
 * 序列管理器
 * 保证并发正确
 * 给定一个序列名称,返回下一个序列号...
 */
class SequenceManager{
    /** @var \Doctrine\DBAL\Connection  */
    protected $connection;
    function __construct(Connection $connection){
        $this->connection = $connection;
    }
    /**
     * 使用某个名称获取下一个序列号(数值)
     * 1,2,3,4,5,...
     * @param integer
     */
    function getNextNum($name,$start_num=1){
        $this->connection->query('set @next=1;');
        $sql = <<<'EOFSQL'
INSERT INTO tbf_sequence (current,name) VALUES (1,:name)
  ON DUPLICATE KEY UPDATE current = (@next := current + 1);
EOFSQL;
        $this->connection->prepare($sql)->execute(array('name'=>$name));
        $data = $this->connection->query('select @next')->fetchAll();
        $num = ArrayFunction::firstValue(ArrayFunction::firstValue($data));
        $num = $num + $start_num-1;
        return $num;
    }

    /**
     * @param $name string
     * @param $start_num int
     * @return string
     * A,B,...,Z,AA,AB,AC,...,AZ,BA,....,ZZ,AAA,...
     */
    function getNextLetter($name,$start_letter='A'){
        $start_num = $this->letterToNum($start_letter);
        $num = $this->getNextNum($name,$start_num);
        return $this->numToLetter($num);
    }

    function numToLetter($num){
        $code = '';
        while (true){
            $this_letter = ($num-1)%26; //1->A->0 ,26->Z->25
            $this_letter = chr(ord('A')+$this_letter);
            $code = $this_letter.$code;
            $num = floor(($num-1)/26);
            if ($num<=0){
                break;
            }
        }
        return $code;
    }
    function letterToNum($letter){
        $num = 0;
        $multi = 1;
        for($i=strlen($letter)-1;$i>=0;$i--){
            $this_letter = substr($letter,$i,1);
            $this_num = ord($this_letter)-ord('A')+1;
            $num = $num + $this_num * $multi;
            $multi = $multi * 26;
        }
        return $num;
    }
}